# Greg Johnston
#  <greg.johnston@gmail.com>
#  calc.py
#  Calculations used in the log
from datetime import date
from math import floor
from sets import Set
import locale
from config import RitualConfig

def string_date(string):
	"""Converts MM/DD/YYYY into a datetime date"""
	mmtup = string.partition("/") # partition returns a tuple of (before, splitter, after)
	ddtup = mmtup[2].partition("/")
	year = ddtup[2]
	month = mmtup[0]
	day = ddtup[0]
	return date(int(year), int(month), int(day))

def date_string(date):
	"""Converts a datetime date into MM/DD/YYYY"""
	return time_str(date.month) + "/" + time_str(date.day) + "/" + str(date.year)

def time_str(n):
	"""Returns a string padded to 2 characters"""
	if n < 10:
		return "0" + str(n)
	else:
		return str(n)

def time_in_seconds(tstr):
	"""Calculates the time in seconds for a given time string"""
	if len(tstr) >= 2:
		seconds = int(tstr[-2:len(tstr)+1])
	else: seconds = 0
	if len(tstr) >= 4:
		minutes = int(tstr[-5:-3])
	else: minutes = 0
	if len(tstr) >= 6:
		hours = int(tstr[0:-6])
	else: hours = 0
	return hours*60*60 + minutes*60 + seconds

def seconds_timestr(seconds):
	"""Creates a time string of HH:MM:SS given a number of seconds"""
	hours = int(floor(seconds/60/60))
	minutes = int(floor((seconds-hours*60*60)/60))
	seconds = int(seconds%60)
	if hours == 0:
		return str(minutes) + ":" + time_str(seconds)
	else:
		return str(hours) + ":" + time_str(minutes) + ":" + time_str(seconds)

def unit_code(unit):
	"""Given a unit, returns its code"""
	if unit == "miles": return 0
	elif unit == "yd": return 1
	elif unit == "yds": return 1 # Coolrunning
	elif unit == "m":  return 2
	elif unit == "meters": return 2 # Coolrunning
	elif unit == "km": return 3
	elif unit == "": return 0
	else: print "Unit: ", unit

def code_unit(unit):
	"""Given a unit code, returns its representation as a string"""
	if unit == 0: return "miles"
	elif unit == 1: return "yd"
	elif unit == 2:  return "m"
	elif unit == 3: return "km"

def calc_miles(distance, unit):
	"""Converts a distance in a unit to miles"""
	distance = float(distance)
	if unit == 0: # miles
		pass
	elif unit == 1: # yd
		distance = distance/1760
	elif unit == 2: # m
		distance = distance/1609.344
	elif unit == 3: # km
		distance = distance/1.61
	else:
		print str(unit) + " is not a proper unit code."
		distance = False
	return distance

def calc_pace(tstr, distance, unit):
	"""Calculates a pace in minutes/mile and returns a string, given a time string a distance, and a unit"""
	distance = calc_miles(distance, unit)
	time = time_in_seconds(tstr)
	if distance == 0: # To prevent zero-division
		return "No distance given"
	else:
		seconds_per_mile = time/distance
		pace_minutes = floor(seconds_per_mile/60)
		pace_seconds = seconds_per_mile % 60
		return str(int(pace_minutes)) + ":" + time_str(int(pace_seconds))

def add_times(lst):
	"""Adds a list of times (as strings) and return the result, as a string"""
	so_far = 0
	for time in lst:
		so_far += time_in_seconds(time)
	return seconds_timestr(so_far)

def week_starting_on_monday(date):
	"""Returns the ordinal week of the date passed, with weeks starting on Monday"""
	return date.isocalendar()[1]
#	isoweek = date.isocalendar()[1]
#	if date.isoweekday() == 7: # Sunday
#		week = isoweek+1 # Starts next week
#	else:
#		week = isoweek
#	if week > 52:
#		week = week-52
#	return week

def is_week_of(date1, date2):
	"""Returns True if date1 is the same ordinal week as date2, False otherwise."""
	week1 = week_starting_on_monday(date1)
	week2 = week_starting_on_monday(date2)
	if week1 == week2 == 1 and abs(date1.year-date2.year) == 0 and (date1.month == 12 or date2.month == 12):
		return False
	elif week1 == week2 == 1 and abs(date1.year-date2.year) == 1 and (date1.month == 12 or date2.month == 12):
		return True
	elif week1 == week2 and date1.year == date2.year:
		return True
	else:
		return False

def dates_week_of(date1):
	"""Returns a list of the dates which are in the same week as date1"""
	so_far = []
	for y in range(date1.year-1, date1.year+1):
		for m in range(date1.month-1, date1.month+1):
			if m == 0: m = 12
			elif m == 13: m = 1
			for d in range(0, 32):
				try:
					if is_week_of(date1, date(y, m, d)) and not so_far.count(date(y, m, d)) > 1:
						so_far += [date(y, m, d)]
				except:
					pass
	return so_far

def merge(a, b):
	"""Merges two dictionaries"""
	for k in a.keys():
		if b.has_key(k): continue
		else: b[k] = a[k]
	return b

def merge_logs(obj1, obj2):
	"""Given two similarly formatted RitualLog-based objects, return a synthesis log and courses"""
	return (merge(obj1.courses, obj2.courses), merge(obj1.log, obj2.log))


def unescape(s):
	"""Unescapes an HTML string to plain text"""
	try:
		s = s.replace("&lt;", "<")
		s = s.replace("&gt;", ">")
		s = s.replace("&amp;", "&")
		s = s.replace("<BR>", "\n")
		s = s.replace("<COMMA>", ",")
		return s
	except:
		return None

def escape(s):
	"""Escapes the commas in a string for CSV use"""
	try:
		s = s.replace("<COMMA>", ",")
		return s
	except:
		return None

def stripzeros(lst):
	"""Strips all zero-values from the end of a list"""
	if len(lst) == 0:
		return lst
	if lst[-1] == 0:
		return stripzeros(lst[0:-1])
	else:
		return lst
